#include <stdio.h>
#include<math.h>
#include "pcb.h"
#include "sim.h"
#include "sched1.h"
#include "doubleL.h"

#define HIGHPRIORIT 8
#define MINTIMESLICE 10
#define EMPTY -1
#define DOUBLE 0
#define HALF 1
#define INIT 2

list* priority_queue[HIGHPRIORIT];
static void findRemove(PCB* p);
static int  firstNonEmpty();
static PCB* nextRun();
static int handleTimeSlice(PCB* p);
static void changePriority(PCB* p);

/*init 8 priority queue*/
void initSched1()
{
	int i;
	for(i=0;i<HIGHPRIORIT;i++)
	{
		priority_queue[i]=list_init();
		if(NULL==priority_queue[i])
		{
			fprintf(stderr, "Fail to init queue\n");
			exit(EXIT_FAILURE);
		}
	}
}



/*
*from high priority queue to low priority,find the first non-empty queue
* return the number of queue
*/
static int  firstNonEmpty()
{
	int index=EMPTY, i;
	for(i=HIGHPRIORIT-1;i>=0;i--)
	{
		if(!(list_empty(priority_queue[i])))
		{
			
			index=i;
			break;
		}
	}
	return index;
}

/*for test, print the whole queue*/
void printQueue()
{
	int i=0;
	for (i = HIGHPRIORIT-1; i>=0; i--)
	{
		printf("priority queue : %d",i);
		list_print(priority_queue[i]);
	}
}
/*find the node with p's pid and remove it*/
static void findRemove(PCB* p)
{
   	list *t;
   	unsigned priority=getPriority(p);
   	t=priority_queue[priority]->next;
   	if(t == priority_queue[priority])
   	    return;
   	if(getPid(t->data)==getPid(p))
    {
  		/*remove node*/
  		t->prev->next=t->next;
        t->next->prev=t->prev;
        free(t);
        return;
    }
    
}

/*select the next process to run or return NULL if queue is empty*/
static PCB* nextRun()
{
    PCB* tmp=NULL;
    int no;
    no=firstNonEmpty();
    if(EMPTY==no)/*find the first no empty queue */
    {
        return NULL;
    }
    else
    {	
        tmp=(PCB*)priority_queue[no]->next->data;
        return tmp;
    }
}


/*
*decide what to do with the program's time slice or priority
* if the program run out whole time slice or use greater than half of its time slice,return DOUBLE
* if  the program uses less than half of its time slice, return HALF 
*/
static int handleTimeSlice(PCB* p)
{
    if(getCompletedSlice(p)<(getTimeSlice(p)/2))
	{
		return HALF;
	}
    else
    {
    	return DOUBLE;
    }
}
/* based on the return value of time_slice()
change the process's priority */
static void changePriority(PCB* p)
{
    int type;
    unsigned priority=getPriority(p);
    unsigned time_slice=getTimeSlice(p);
	setTimeSlice(p,pow(2,HIGHPRIORIT-priority-1)*MINTIMESLICE);
	type=handleTimeSlice(p);
	switch (type)
	{
        case DOUBLE:  
        if(priority>0)
        {
            setPriority(p,priority - 1);
            setTimeSlice(p,time_slice*2);
	    }
	    break;
        case HALF:      
        if(p->priority < HIGHPRIORIT-1)
        {
            setPriority(p,priority + 1);
            setTimeSlice(p,time_slice/2);
	    }
	    break;
		default:
		fprintf(stderr, "Wrong Event\n");
		exit(EXIT_FAILURE);      
	}
	/*printf("current process :%d,%u,%d,%d,%d,time_slice:%d \n",p->pid,p->priority,p->completed_cpu_time,p->cpu_time,p->time_slice,time_slice(p));*/
	
}
/*handle the event passed by simulator*/
PCB* handleEvent1(Event e, PCB* p)
{

    PCB* tmp;    
	switch(e)
	{
		case TERMPROC:
            findRemove(p);/*remove for ever*/
		    break;
		    
		case IOSTART:
		    findRemove(p);/*will move back*/
			break;
		case TERMSLICE:
		case IOCOMPLETE:
            if(!isProcessComplete(p))
		    {
		        findRemove(p);
		        changePriority(p);/*change pcb' priority and put in the right queue*/
		        list_append(priority_queue[getPriority(p)],(ElemType)p);
		    }
		   
		    break;
		case PROCESSSTART:
			 if((0==getTimeSlice(p))&&(0==getCompletedTime(p)))
    		{
        	     /*init pcb's priority and timeslice*/
        	    setPriority(p,HIGHPRIORIT-1);
			    setTimeSlice(p,MINTIMESLICE);
			    list_append(priority_queue[getPriority(p)],(ElemType)p);
    		}
			break;
		default:
			fprintf(stderr, "Wrong Event\n");
			exit(EXIT_FAILURE);

	}
	/*return the next program to run*/
	tmp=nextRun();
	return tmp;

}


